package registry

import (
	"context"
	"crypto/rsa"
	"cybertwin/db/session"
	db "cybertwin/db/sqlc"
	"cybertwin/pb/model"
	"cybertwin/utils/configtools"
	"cybertwin/utils/cryptotools"
	"cybertwin/utils/generator"
	"cybertwin/utils/protocol"
	"fmt"
	"sync/atomic"

	"github.com/panjf2000/gnet/v2"
	"github.com/panjf2000/gnet/v2/pkg/logging"
	log "github.com/sirupsen/logrus"
	"github.com/sqlc-dev/pqtype"
	"google.golang.org/protobuf/proto"
)

var RegisterCenterPrivateKey *rsa.PrivateKey

func init() {
	// 生成注册中心需要使用的密钥对,保存到目录./ca/registry
	//keypair1 := cryptotools.RSA_GenKey(2048)
	//saveDir := "./ca/registry"
	//cryptotools.RSA_SavePublicKey(saveDir, "publicKey.pem", &keypair1.PublicKey)
	//cryptotools.RSA_SavePrivateKey(saveDir, "privateKey.pem", &keypair1.PrivateKey)

	dirLocation := configtools.GlobalConfiguration.GetString("crypt.registerCenter.dirLocation")
	privateKeyFileName := configtools.GlobalConfiguration.GetString("crypt.registerCenter.skName")
	log.Infoln("[注册中心]-[init]-加载私钥文件" + dirLocation + "/" + privateKeyFileName)
	RegisterCenterPrivateKey = cryptotools.RSA_LoadPrivateKey(dirLocation, privateKeyFileName)
}

type RegisterCenter struct {
	gnet.BuiltinEventEngine
	eng          gnet.Engine
	Network      string
	Addr         string
	Multicore    bool
	connected    int32
	disconnected int32
}

func (s *RegisterCenter) OnBoot(eng gnet.Engine) (action gnet.Action) {
	logging.Infof("running RegisterCenter on %s with multi-core=%t",
		fmt.Sprintf("%s://%s", s.Network, s.Addr), s.Multicore)
	s.eng = eng
	return
}

func (s *RegisterCenter) OnOpen(c gnet.Conn) (out []byte, action gnet.Action) {
	c.SetContext(new(protocol.AccessProtocolCodec))
	atomic.AddInt32(&s.connected, 1)
	log.WithFields(log.Fields{
		"remote-peer-ip": c.RemoteAddr().String(),
	}).Infoln("连接成功")
	return
}

func (s *RegisterCenter) OnClose(c gnet.Conn, err error) (action gnet.Action) {
	if err != nil {
		logging.Infof("error occurred on connection=%s, %v\n", c.RemoteAddr().String(), err)
	}
	disconnected := atomic.AddInt32(&s.disconnected, 1)
	connected := atomic.AddInt32(&s.connected, -1)
	if connected == 0 {
		logging.Infof("all %d connections are closed", disconnected)
	}
	return
}

func (s *RegisterCenter) OnShutdown(e gnet.Engine) {
	log.Infoln("gnet shutting down...")
}

func (s *RegisterCenter) OnTraffic(c gnet.Conn) (action gnet.Action) {
	codec := c.Context().(*protocol.AccessProtocolCodec)
	var packets [][]byte
	for {
		cryptData, typeName, isEncrypt, err := codec.Decode(c)
		log.Infoln("成功解析数据包")

		// tcp半包，break掉等待下一次事件触发
		if err == protocol.ErrIncompleteBuf {
			break
		}

		if err != nil {
			logging.Errorf("invalid packet: %v", err)
			return gnet.None
		}

		switch typeName {
		case "TYPE_REGISTER":
			// data反序列化为注册参数，然后将注册结果写入数据库中，返回结果提示设备注册成功
			var registerParam = model.RegisterParam{}

			// 此时的cryptData是否加密，要看isEncrypt是否为true
			var data []byte
			if isEncrypt {
				data, err = cryptotools.RSA_Decrypt(cryptData, RegisterCenterPrivateKey)
			} else {
				data = cryptData
			}

			if err != nil {
				log.Errorln("[注册中心]-[OnTraffic()]-使用私钥解密失败:" + err.Error())
				return gnet.Close
			}

			err = proto.Unmarshal(data, &registerParam)
			if err != nil {
				log.Errorln("设备注册请求的data无法序列化")
				// 此处先暂时忽略错误，因为错误类型为data太长所导致的报错
				marshal, _ := proto.Marshal(&model.RegisterResp{
					MsgId: "1",
					Msg:   "设别注册请求的数据异常，注册失败",
				})
				encodeData, _ := codec.Encode(marshal, protocol.TYPE_RESPONSE)
				packets = append(packets, encodeData)
				break
			}
			log.Infoln("设备注册参数: " + registerParam.String())

			// 尝试进行注册
			resp, err := tryRegisterUe(&registerParam)
			if err != nil {
				logging.Errorf("设备注册失败", err)
				return gnet.None
			}

			// 生成注册成功结果
			registerResp := model.RegisterResp{
				MsgId:       "123",
				UeId:        resp.UeID,
				Msg:         "设备注册成功",
				CybertwinId: resp.CybertwinID,
			}
			registerRespBytes, err := proto.Marshal(&registerResp)

			if err != nil {
				logging.Errorf("设备response序列化失败", err)
				return gnet.Close
			}

			packet, _ := codec.Encode(registerRespBytes, protocol.TYPE_RESPONSE)
			packets = append(packets, packet)
		case "TYPE_USER_REGISTER":
			// data反序列化为注册参数，然后将注册结果写入数据库中，返回结果提示设备注册成功
			var userRegisterParam = model.UserRegisterParam{}

			// 此时的cryptData是否加密，要看isEncrypt是否为true
			var data []byte
			if isEncrypt {
				data, err = cryptotools.RSA_Decrypt(cryptData, RegisterCenterPrivateKey)
			} else {
				data = cryptData
			}

			if err != nil {
				log.Errorln("[注册中心]-[OnTraffic()]-使用私钥解密失败:" + err.Error())
				return gnet.Close
			}

			err = proto.Unmarshal(data, &userRegisterParam)
			if err != nil {
				log.Errorln("用户注册请求的data无法序列化")
				// 此处先暂时忽略错误，因为错误类型为data太长所导致的报错
				marshal, _ := proto.Marshal(&model.UserRegisterResp{
					MsgId: "1",
					Msg:   "用户注册请求的数据异常，注册失败",
				})
				encodeData, _ := codec.Encode(marshal, protocol.TYPE_USER_RESPONSE)
				packets = append(packets, encodeData)
				break
			}
			log.Infoln("用户注册参数: " + userRegisterParam.String())

			// 尝试进行注册
			resp, ctid, err := tryRegisterUser(&userRegisterParam)
			if err != nil {
				logging.Errorf("用户注册失败", err)
				return gnet.None
			}

			// 生成注册成功结果
			registerResp := model.UserRegisterResp{
				MsgId:       "123",
				Msg:         "用户注册成功",
				Uid:         resp.Uid,
				CybertwinId: ctid,
			}
			registerRespBytes, err := proto.Marshal(&registerResp)

			if err != nil {
				logging.Errorf("用户response序列化失败", err)
				return gnet.Close
			}

			packet, _ := codec.Encode(registerRespBytes, protocol.TYPE_USER_RESPONSE)
			packets = append(packets, packet)

		default:
			log.Errorln("非注册类型的数据包")
			return gnet.None
		}
	}
	if n := len(packets); n > 1 {
		_, _ = c.Writev(packets)
	} else if n == 1 {
		_, _ = c.Write(packets[0])
	}
	return
}

func tryRegisterUe(param *model.RegisterParam) (resp db.InsertRegisteredUeRow, err error) {
	// 尝试注册
	//var _ *db.Queries
	//var testDB *sql.DB
	//_ = db.New(testDB)

	var macAddr pqtype.Macaddr
	err = macAddr.Scan(param.Mac)
	if err != nil {
		log.Errorln("mac地址解析失败:" + param.Mac)
		return db.InsertRegisteredUeRow{}, err
	}

	//query = session.NewDBQuery()
	//ue, err := query.InsertRegisteredUe(context.Background(), db.InsertRegisteredUeParams{
	//	Mac:         macAddr,
	//	Uename:      param.UeName,
	//	UeType:      param.UeType,
	//	Cybertwinid: param.CybertwinId,
	//	Ueid:        generator.GenUeUUID(),
	//})
	dbtxQuery := session.NewDBTXQuery()
	insertRegisterUeParam := db.InsertRegisteredUeParams{
		Mac:         macAddr,
		Uename:      param.UeName,
		UeType:      param.UeType,
		Cybertwinid: param.CybertwinId,
		Ueid:        generator.GenUeUUID(),
		Uepk:        param.UePublicKey,
		Hid:         param.Hid,
	}
	txRes, err := dbtxQuery.UeRegisterTX(context.Background(), session.UeRegisterTXParam{
		InsertRegisteredUeParam: insertRegisterUeParam,
		Uid:                     param.Uid,
	})
	if err != nil {
		return db.InsertRegisteredUeRow{}, err
	}
	log.WithFields(log.Fields{
		"ueid":        txRes.UeID,
		"cybertwinId": txRes.CybertwinID,
		"uid":         param.Uid,
	}).Infoln("[registry_center]-[tryRegisterUe()]-成功注册设备")
	return txRes, nil
}

func tryRegisterUser(param *model.UserRegisterParam) (resp db.InsertUserByIdParams, ctid string, err error) {
	dbtxQuery := session.NewDBTXQuery()
	insertUserByIdParams := db.InsertUserByIdParams{
		Uid:      generator.GenUidUUID(),
		Username: param.Username,
	}
	txRes, err := dbtxQuery.UserRegisterTX(context.Background(), insertUserByIdParams)

	if err != nil {
		return db.InsertUserByIdParams{}, "", err
	}
	log.WithFields(log.Fields{
		"cybertwinId": txRes.CybertwinID,
		"username":    param.Username,
		"uid":         txRes.Uid,
	}).Infoln("[registry_center]-[tryRegisterUser()]-成功注册用户")
	return insertUserByIdParams, txRes.CybertwinID, nil
}
